/*
 * Force.java
 *
 * Copyright 2008 the Desert Labs Project (http://desertlabs.us)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package us.desertlabs.physics;

import java.math.BigDecimal;
import us.desertlabs.number.BigUtility;

/**
 * This is used to represent an amount of force. Force is required to accelerate
 * mass.
 *
 * @author Brandon Franklin
 */
public class Force extends DimensionalValue {

    /**
     * Given a <CODE>Mass</CODE> and <CODE>Acceleration</CODE>, calculates
     * and returns the corresponding amount of <CODE>Force</CODE>.
     *
     * @param mass the <CODE>Mass</CODE>
     * @param acceleration the <CODE>Acceleration</CODE>
     * @return the <CODE>Force</CODE> resulting from accelerating the <CODE>Mass</CODE>
     *         at the supplied rate
     * @throws IllegalArgumentException if either of the parameters is invalid
     *         for any reason
     */
    static public Force calculate( final Mass mass, final Acceleration acceleration ) {

        // Validate the mass
        if( mass == null ) {
            throw new IllegalArgumentException( "Mass cannot be null." );
        }

        // Validate the acceleration
        if( acceleration == null ) {
            throw new IllegalArgumentException( "Acceleration cannot be null." );
        } else if( !acceleration.isValid() ) {
            throw new IllegalArgumentException( "Acceleration is internally invalid." );
        }

        final BigDecimal bigDecMass = new BigDecimal( mass.toString() );
        final Number[] amounts = new Number[acceleration.getDimensionality()];
        final Number[] rates = acceleration.getRates();
        for( int i = 0; i < amounts.length; i++ ) {
            final BigDecimal rate = new BigDecimal( rates[i].toString() );
            amounts[i] = BigUtility.clipScale( bigDecMass.multiply( rate ) );
        }

        return new Force( amounts );
    }

    /**
     * Creates a new instance of <CODE>Force</CODE> with the specified amounts
     * of force in each dimension.
     *
     * @param values the amounts of force in each dimension
     * @throws IllegalArgumentException if the parameter is null or contains any
     *         null elements
     */
    public Force( final Number[] values ) {
        super( values );
    }

    /**
     * Given a <CODE>Mass</CODE>, returns the <CODE>Acceleration</CODE>
     * resulting from applying this <CODE>Force</CODE>.
     *
     * @param mass the <CODE>Mass</CODE> to apply the <CODE>Force</CODE> to
     * @return the resulting <CODE>Acceleration</CODE>
     * @throws IllegalArgumentException if the parameter is invalid for any
     *         reason
     */
    public Acceleration applyToMass( final Mass mass ) {

        // Validate the mass
        if( mass == null ) {
            throw new IllegalArgumentException( "Mass cannot be null." );
        }

        // Do the application
        final Number[] values = getValues();
        final Number[] results = new Number[values.length];
        final BigDecimal bigDecMass = new BigDecimal( mass.toString() );
        for( int i = 0; i < results.length; i++ ) {
            BigDecimal bigDecValue = new BigDecimal( values[i].toString() );
            bigDecValue = BigUtility.normalizeScale( bigDecValue );
            results[i] = bigDecValue.divide( bigDecMass, BigDecimal.ROUND_HALF_UP );
        }

        return new Acceleration( results );
    }
}
